/* map_data.cpp */

#include <dodo_plot/map/map_data.h>

#include <iostream>
#include <cstdlib>

namespace dodo_plot
{

// Map_Data constructor etc,
// smart ptr with Map_Data_Impl

Map_Data::Map_Data(const std::string& filename)
		: impl_(new Map_Data_Impl(filename))
{
}

Map_Data::Map_Data(const std::string& filename,
                   double lon_min, double lon_max,
                   double lat_min, double lat_max)
		: impl_(new Map_Data_Impl(filename,
		                          lon_min, lon_max,
		                          lat_min, lat_max))
{
}

Map_Data::Map_Data(const Map_Data& orig)
		: impl_(orig.impl_)
{
	orig.impl_->use_count_++;
}

Map_Data::~Map_Data()
{
	if (--impl_->use_count_ == 0)
		delete impl_;
}

Map_Data& Map_Data::operator= (const Map_Data& orig)
{
	orig.impl_->use_count_++;
	if (--impl_->use_count_ == 0)
		delete impl_;
	impl_ = orig.impl_;
	return *this;
}

Map_Data::iterator Map_Data::begin()
{
	return impl_->begin();
}

Map_Data::const_iterator Map_Data::begin() const
{
	return impl_->begin();
}

Map_Data::iterator Map_Data::end()
{
	return impl_->end();
}

Map_Data::const_iterator Map_Data::end() const
{
	return impl_->end();
}

int Map_Data::size() const
{
	return impl_->size();
}

int Map_Data::shape_type() const
{
	return impl_->shape_type();
}

double Map_Data::lon_min() const
{
	return impl_->lon_min();
}

double Map_Data::lon_max() const
{
	return impl_->lon_max();
}

double Map_Data::lat_min() const
{
	return impl_->lat_min();
}

double Map_Data::lat_max() const
{
	return impl_->lat_max();
}

//Map_Data_Impl

Map_Data_Impl::Map_Data_Impl(const std::string& filename)
	: use_count_(1), shape_type_(0)
{
	load_from_file(filename, false);
}

Map_Data_Impl::Map_Data_Impl(const std::string& filename,
                             double lon_min, double lon_max,
                             double lat_min, double lat_max)
	: use_count_(1), shape_type_(0),
	lon_min_(lon_min), lon_max_(lon_max),
	lat_min_(lat_min), lat_max_(lat_max)
{
	load_from_file(filename, true);
}

Map_Data_Impl::~Map_Data_Impl()
{
	for(iterator iter = begin(); iter != end(); ++iter)
	{
		SHPDestroyObject(*iter);
	}
}

void Map_Data_Impl::load_from_file(const std::string& filename,
                    bool check_range
                   )
{
	bool cross_180 = (lon_min_ > lon_max_) ? true : false;

//Part of the following code are edited based on codes from shapelib.maptools.org

	SHPHandle   hSHP;
	int         nShapeType, nEntities, i, iPart, bValidate = 0, nInvalidCount = 0;
//	const char  *pszPlus;
	double      adfMinBound[4], adfMaxBound[4];
	/* -------------------------------------------------------------------- */
	/*      Open the passed shapefile.                                      */
	/* -------------------------------------------------------------------- */
	hSHP = SHPOpen(filename.c_str(), "rb");

	if (hSHP == NULL)
	{
		std::cout << "Unable to open: " << filename << std::endl;
		exit(0);
		// TODO throw something instead of exit...
	}

	/* -------------------------------------------------------------------- */
	/*      Print out the file bounds.                                      */
	/* -------------------------------------------------------------------- */
	SHPGetInfo(hSHP, &nEntities, &nShapeType, adfMinBound, adfMaxBound);

	shape_type_ = nShapeType;
	
	if(!check_range)
	{
		lon_min_ = adfMinBound[0];
		lon_max_ = adfMaxBound[0];
		lat_min_ = adfMinBound[1];
		lat_max_ = adfMaxBound[1];
	}
	
	/* -------------------------------------------------------------------- */
	/*      Skim over the list of shapes, printing all the vertices.        */
	/* -------------------------------------------------------------------- */

	for (i = 0; i < nEntities; i++)
	{
		SHPObject       *psShape;

		psShape = SHPReadObject(hSHP, i);

		if(check_range)
		{
			double shp_x_min = psShape->dfXMin;
			double shp_x_max = psShape->dfXMax;
			double now_lon_min = lon_min_;
			double now_lon_max = lon_max_;
			if(cross_180)
			{	
				shp_x_min = adjust_cross_180(psShape->dfXMin);
				shp_x_max = adjust_cross_180(psShape->dfXMax);
	//			now_lon_min = adjust_cross_180(lon_min_);
				now_lon_max = adjust_cross_180(lon_max_);
			}
	
			// if part of the shape is in the limit range,
			if( 
				!( shp_x_min > now_lon_max || shp_x_max < now_lon_min 
					||
					psShape->dfYMin > lat_max_ || psShape->dfYMax < lat_min_ )
			  )
			{
				data_.push_back(psShape);			
			}
			else
			{
				SHPDestroyObject(psShape);
			}
		}
		else
		{
			data_.push_back(psShape);
		}
	}

	SHPClose( hSHP );
}

double Map_Data_Impl::adjust_cross_180(double lon) const
{
	return lon < lon_min_ ? lon + 360.0 : lon;
}

Map_Data_Impl::iterator Map_Data_Impl::begin()
{
	return data_.begin();
}

Map_Data_Impl::const_iterator Map_Data_Impl::begin() const
{
	return data_.begin();
}

Map_Data_Impl::iterator Map_Data_Impl::end()
{
	return data_.end();
}

Map_Data_Impl::const_iterator Map_Data_Impl::end() const
{
	return data_.end();
}

int Map_Data_Impl::size() const
{
	return data_.size();
}

int Map_Data_Impl::shape_type() const
{
	return shape_type_;
}

double Map_Data_Impl::lon_min() const
{
	return lon_min_;
}

double Map_Data_Impl::lon_max() const
{
	return lon_max_;
}

double Map_Data_Impl::lat_min() const
{
	return lat_min_;
}

double Map_Data_Impl::lat_max() const
{
	return lat_max_;
}

}
